﻿using System;
using System.Collections.Generic;

using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

/*
 * No Permission for this assembly should be granted (except execution)
 * because it executes unknown user python code.
 */
namespace IdeaNMR.Shared.ScriptingEngine
{
    public class PythonInterpreter
    {
        #region Manage Engine
        private ScriptEngine _engine;
        private ScriptScope _scope;

        public void AddVariable(string varName, object varObject)
        {
            _scope.SetVariable(varName, varObject);
        }
        public object GetVariable(string varName)
        {
            return _scope.GetVariable(varName);
            //return null;
        }

        public void Evaluate(string Instructions)
        {
            // evaluate the given code
            // using "randomName" as the result variable name
            Evaluate(Instructions, "randomName");
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Instructions"></param>
        /// <param name="ResultVariableName"></param>
        /// <returns></returns>
        /// <exception cref="IdeaNMR.Shared.ScriptingEngine.PythonSyntaxException">Thrown when ScriptExecute throws SyntaxErrorException.</exception>
        /// <exception cref="IdeaNMR.Core.ParameterException">Thrown when bad parameter is used with SetupScope functions.</exception>
        // ALISTAIR: comments added 10/07/2011
        // Evaluate the given code
        public string Evaluate(string Instructions, string ResultVariableName)
        {
            string finalResult = null;
            // Convert the Instructions string into Python statements
            ScriptSource source = _engine.CreateScriptSourceFromString(Instructions, SourceCodeKind.Statements);
            try
            {
                // execute the python source code
                source.Execute(_scope);
            }
            catch (SyntaxErrorException ex)
            {
                ExceptionOperations eo;
                eo = _engine.GetService<ExceptionOperations>();
                string error = eo.FormatException(ex);
                throw new PythonSyntaxErrorException(error, ex);
            }
            catch (IronPython.Runtime.UnboundNameException ex)
            {
                string msg = string.Format("UnboundNameException was thrown during python script evaluation. Message: '{0}'.", ex.Message);
                PythonRuntimeException pex = new PythonRuntimeException(msg);
                throw pex;
            }
            catch (ArgumentTypeException ex)
            {
                string msg = string.Format("ArgumentTypeException was thrown during python script evaluation. Message: '{0}'.", ex.Message);
                PythonRuntimeException pex = new PythonRuntimeException(msg);
                throw pex;
            }

            if (!_scope.ContainsVariable(ResultVariableName))
            {
                finalResult = "The variable FinalResult is not defined in user code.";
            }
            else
            {
                finalResult = _scope.GetVariable<object>(ResultVariableName).ToString();
            }
            return finalResult;
        }

        #endregion

        public PythonInterpreter()
        {
            string[] pythonModules = new string[] { "math", "cmath", "datetime", "array", "re" };

            Dictionary<string, object> engineOptions = new Dictionary<string, object>();
            engineOptions["DivisionOptions"] = PythonDivisionOptions.New; // Without this option 1/2 will give 0.

            _engine = Python.CreateEngine(engineOptions);

            foreach (string module in pythonModules) _engine.ImportModule(module);

            _scope = _engine.CreateScope();
        }
    }

    public class PythonSyntaxErrorException : ApplicationException
    {
        #region Inherited Constructors
        public PythonSyntaxErrorException()
            : base("Syntax error in the python script.")
        { }
        public PythonSyntaxErrorException(string message)
            : base(message)
        { }
        public PythonSyntaxErrorException(string message, System.Exception innerException)
            : base(message, innerException)
        { }

        protected PythonSyntaxErrorException(System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context)
            : base(info, context)
        { }
        #endregion
    }

    public class PythonRuntimeException : ApplicationException
    {
        #region Inherited Constructors
        public PythonRuntimeException()
            : base("Error executing python script.")
        { }
        public PythonRuntimeException(string message)
            : base(message)
        { }
        public PythonRuntimeException(string message, System.Exception innerException)
            : base(message, innerException)
        { }

        protected PythonRuntimeException(System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context)
            : base(info, context)
        { }
        #endregion
    }
}

